#include "stdafx.h"
#include "net/QuotedPrintableEncode.hpp"
#include "utils/numberformatter.hpp"
#include "utils/numberparser.hpp"
using JHCPP::utils::CNumberFormatter;
using JHCPP::utils::CNumberParser;
#include "charset/ascii.hpp"
using JHCPP::charset::CAscii;
#include "stdlib/exception.hpp"
using JHCPP::stdlib::jhException::DataFormatException;

NET_NAMESPACE_BEGIN

CQuotedPrintableEncodeBuf::CQuotedPrintableEncodeBuf(std::ostream& ostr)
	: m_pending(-1),
	m_lineLength(0),
	m_ostr(ostr)
{
}

CQuotedPrintableEncodeBuf::~CQuotedPrintableEncodeBuf()
{
	try
	{
		close();
	}
	catch (...)
	{
	}
}

int CQuotedPrintableEncodeBuf::writeToDevice(char c)
{
	if (m_pending != -1)
	{
		if (m_pending == '\r' && c == '\n')
			writeRaw((char) m_pending);
		else if (c == '\r' || c == '\n')
			writeEncoded((char) m_pending);
		else
			writeRaw((char) m_pending);
		m_pending = -1;
	}
	if (c == '\t' || c == ' ')
	{
		m_pending = charToInt(c);
		return m_pending;
	}
	else if (c == '\r' || c == '\n' || (c > 32 && c < 127 && c != '='))
	{
		writeRaw(c);
	}
	else
	{
		writeEncoded(c);
	}
	return charToInt(c);
}


void CQuotedPrintableEncodeBuf::writeEncoded(char c)
{
	if (m_lineLength >= 73)
	{
		m_ostr << "=\r\n";
		m_lineLength = 3;
	}
	else m_lineLength += 3;
	m_ostr << '=' << CNumberFormatter::formatHex((unsigned) charToInt(c), 2);
}

void CQuotedPrintableEncodeBuf::writeRaw(char c)
{
	if (c == '\r' || c == '\n')
	{
		m_ostr.put(c);
		m_lineLength = 0;
	}
	else if (m_lineLength < 75)
	{
		m_ostr.put(c);
		++m_lineLength;
	}
	else
	{
		m_ostr << "=\r\n" << c;
		m_lineLength = 1;
	}
}

int CQuotedPrintableEncodeBuf::close()
{
	sync();
	return m_ostr ? 0 : -1;
}


CQuotedPrintableEncodeIOS::CQuotedPrintableEncodeIOS(std::ostream& ostr) : m_buf(ostr)
{
	
}

CQuotedPrintableEncodeIOS::~CQuotedPrintableEncodeIOS()
{
}

int CQuotedPrintableEncodeIOS::close()
{
	return m_buf.close();
}

CQuotedPrintableEncodeBuf* CQuotedPrintableEncodeIOS::rdbuf()
{
	return &m_buf;
}


CQuotedPrintableEncode::CQuotedPrintableEncode(std::ostream& ostr)
	: CQuotedPrintableEncodeIOS(ostr), 
	std::ostream(&m_buf)
{
}

CQuotedPrintableEncode::~CQuotedPrintableEncode()
{
}



CQuotedPrintableDecodeBuf::CQuotedPrintableDecodeBuf(std::istream& istr) : m_buf(*istr.rdbuf())
{
}

CQuotedPrintableDecodeBuf::~CQuotedPrintableDecodeBuf()
{
}

int CQuotedPrintableDecodeBuf::readFromDevice()
{
	int ch = m_buf.sbumpc();
	while (ch == '=')
	{
		ch = m_buf.sbumpc();
		if (ch == '\r')
		{
			ch = m_buf.sbumpc(); // read \n
		}
		else if (CAscii::isHexDigit(ch))
		{
			std::string hex = "0x";
			hex += (char) ch;
			ch = m_buf.sbumpc();
			if (CAscii::isHexDigit(ch))
			{
				hex += (char) ch;
				return CNumberParser::parseHex(hex);
			}
			throw DataFormatException("Incomplete hex number in quoted-printable encoded stream");
		}
		else if (ch != '\n')
		{
			throw DataFormatException("Invalid occurrence of '=' in quoted-printable encoded stream");
		}
		ch = m_buf.sbumpc();
	}
	return ch;
}


CQuotedPrintableDecodeIOS::CQuotedPrintableDecodeIOS(std::istream& istr) : m_buf(istr)
{

}


CQuotedPrintableDecodeIOS::~CQuotedPrintableDecodeIOS()
{
}


CQuotedPrintableDecodeBuf* CQuotedPrintableDecodeIOS::rdbuf()
{
	return &m_buf;
}


CQuotedPrintableDecode::CQuotedPrintableDecode(std::istream& istr)
	: CQuotedPrintableDecodeIOS(istr), 
	std::istream(&m_buf)
{
}


CQuotedPrintableDecode::~CQuotedPrintableDecode()
{
}


NET_NAMESPACE_END
